package com.eat.fish_service.webSocket;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.eat.common_utils.R;
import com.eat.fish_service.entity.Dao.User;
import com.eat.fish_service.mapper.UserMapper;
import com.eat.fish_service.service.ExpertCheckService;
import com.eat.fish_service.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ServerEndpoint 通过这个 spring boot 就可以知道你暴露出去的 websockst 应用的路径，有点类似我们经常用的@RequestMapping。比如你的启动端口是8080，而这个注解的值是ws，那我们就可以通过 ws://127.0.0.1:8080/websocket 来连接你的应用
 * @OnOpen 当 websocket 建立连接成功后会触发这个注解修饰的方法，注意它有一个 Session 参数
 * @OnClose 当 websocket 建立的连接断开后会触发这个注解修饰的方法，注意它有一个 Session 参数
 * @OnMessage 当客户端发送消息到服务端时，会触发这个注解修改的方法，它有一个 String 入参表明客户端传入的值
 * @OnError 当 websocket 建立连接时出现异常会触发这个注解修饰的方法，注意它有一个 Session 参数
 */
@Component
@ServerEndpoint("/webSocket/newsTips/expertCheck/{username}")
@Slf4j
public class WebSocketServer {

    private static WebSocketServer webSocketServer;

    @Autowired
    private UserService userService;

    @Autowired
    private ExpertCheckService expertCheckService;

    @PostConstruct
    public void init() {
        webSocketServer = this;
    }

    /**
     * concurrent包的线程安全Set，用来存放每个用户对应的Session对象。
     */
    private static Map<String, Session> clients = new ConcurrentHashMap<>();

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(@PathParam("username") String username, Session session) throws IOException {
        if (username == null) {
            return;
        }
        clients.put(username, session);
        System.out.println("用户：" + username + "已连接到websocke服务器");

        User user = webSocketServer.userService.getOne(new QueryWrapper<User>().eq("username", username));
        //判断该用户是否是管理员或超级管理员
        if (user.getStatus() == 3 || user.getStatus() == 4) {
            Integer count = webSocketServer.expertCheckService.checkSize();
            R r = R.ok().data("count", count);
            sendMessageTo(r, session);
        } else {
            R r = R.error().message("不是管理员/超级管理员，断开连接");
            sendMessageTo(r, session);
            session.close();
        }


    }

    /**
     * 返回给所有管理员有多少要审核的消息
     */
    public void newsCount() {
        int count = expertCheckService.checkSize();
        R r = R.ok().data("count", count);
        clients.forEach((key, value) -> {
            try {
                sendMessageTo(r,value);
            } catch (IOException e) {
                log.error("发送给客户端消息失败");
                e.printStackTrace();
            }
        });
    }


    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(@PathParam("username") String username) throws IOException {
        clients.remove(username);
        System.out.println("用户：" + username + "已离开websocket服务器");
    }

    /**
     * 收到客户端消息后调用的方法
     */
    @OnMessage
    public void onMessage(String json) throws IOException {
        System.out.println("前端发送的信息为：" + json);
    }

    /**
     * 出现异常触发的方法
     */
    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }

    /**
     * 单发给某人
     */
    public void sendMessageTo(R message, Session session) throws IOException {
        session.getBasicRemote().sendText(JSONUtil.toJsonStr(message));
    }

    //测试
    public int getClientsCount(){
        return clients.size();
    }
}